package elicecn.web.struts2.dao;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.ClassUtils;

import elicecn.util.BeanUtils;
import elicecn.web.struts2.dto.BaseDto;
import elicecn.web.struts2.dto.Pagination;
import elicecn.web.struts2.exception.BusinessException;
import elicecn.web.struts2.model.BaseModel;

public abstract class BaseDaoImpl<T extends BaseModel, PK extends Serializable>
		extends SqlMapClientDaoSupport implements BaseDao<T, PK> {

	/**
	 * Log variable for all child classes. Uses LogFactory.getLog(getClass())
	 * from Commons Logging
	 */
	protected final Log log = LogFactory.getLog(getClass());
	private Class<T> persistentClass;

	/**
	 * Constructor that takes in a class to see which type of entity to persist
	 * 
	 * @param persistentClass
	 *            the class type you'd like to persist
	 */
	public BaseDaoImpl(final Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		return getSqlMapClientTemplate().queryForList(
				IBatisDaoUtils.getAllObjectQuery(ClassUtils
						.getShortName(this.persistentClass)), null);
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T get(PK id) {
		T object = (T) getSqlMapClientTemplate().queryForObject(
				IBatisDaoUtils.getObjectQuery(ClassUtils
						.getShortName(this.persistentClass)), id);
		if (object == null) {
			log.warn("Uh oh, '" + this.persistentClass + "' object with id '"
					+ id + "' not found...");
			// throw new
			// ObjectRetrievalFailureException(ClassUtils.getShortName(this.persistentClass),
			// id);
			BusinessException e = new BusinessException("D0000002");
			e.addParam(ClassUtils.getShortName(this.persistentClass));
			e.addParam(id.toString());
			throw e;
		}
		return object;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public boolean exists(PK id) {
		T object = (T) getSqlMapClientTemplate().queryForObject(
				IBatisDaoUtils.getObjectQuery(ClassUtils
						.getShortName(this.persistentClass)), id);
		return object != null;
	}

	/**
	 * {@inheritDoc}
	 */
	public T save(final T object) {
		String className = ClassUtils.getShortName(object.getClass());
		Object primaryKey = IBatisDaoUtils.getPrimaryKeyValue(object);
		int affectRecNum = 0;

		String keyId = null;

		// check for null id
		if (primaryKey != null && ((Long) primaryKey).longValue() != 0L) {
			keyId = primaryKey.toString();
		}

		// check for new record
		if (StringUtils.isBlank(keyId)) {
			IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
			primaryKey = getSqlMapClientTemplate().insert(
					IBatisDaoUtils.getInsertQuery(className), object);
			IBatisDaoUtils.setPrimaryKey(object, primaryKey);
		} else {
			IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
			affectRecNum = getSqlMapClientTemplate().update(
					IBatisDaoUtils.getUpdateQuery(className), object);
			if (affectRecNum == 0) {
				BusinessException e = new BusinessException("D0000001");
				String parma = (primaryKey == null || ((Long) primaryKey)
						.longValue() != 0L) ? null : primaryKey.toString();
				e.addParam(parma);
				throw e;
			}
		}

		// check for null id
		if (IBatisDaoUtils.getPrimaryKeyValue(object) == null) {
			// throw new ObjectRetrievalFailureException(className, object);
			log.warn("Uh oh, '" + this.persistentClass + "' object with id '"
					+ primaryKey + "' not found...");

			BusinessException e = new BusinessException("D0000002");
			e.addParam(ClassUtils.getShortName(this.persistentClass));
			String parma = (primaryKey == null || ((Long) primaryKey)
					.longValue() != 0L) ? null : primaryKey.toString();
			e.addParam(parma);
			throw e;
		} else {
			return object;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void updateSelective(T object) {
		String className = ClassUtils.getShortName(object.getClass());
		Object primaryKey = IBatisDaoUtils.getPrimaryKeyValue(object);

		// check for null id
		if (primaryKey != null && ((Long) primaryKey).longValue() != 0L) {
			IBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
			getSqlMapClientTemplate().update(
					IBatisDaoUtils.getUpdateSelectiveQuery(className), object);
		} else {
			// throw new ObjectRetrievalFailureException(className, object);
			log.warn("Uh oh, '" + this.persistentClass + "' object with id '"
					+ primaryKey + "' not found...");

			BusinessException e = new BusinessException("D0000002");
			e.addParam(ClassUtils.getShortName(this.persistentClass));
			String parma = (primaryKey == null || ((Long) primaryKey)
					.longValue() != 0L) ? null : primaryKey.toString();
			e.addParam(parma);
			throw e;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void remove(PK id) {
		getSqlMapClientTemplate().update(
				IBatisDaoUtils.getRemoveQuery(ClassUtils
						.getShortName(this.persistentClass)), id);
	}

	@SuppressWarnings("unchecked")
	public Pagination findObjectsWithPg(String statementName, BaseDto dto) {
		Pagination pg = new Pagination();

		// dto.setNeedCount(true);
		Object count = getSqlMapClientTemplate().queryForObject(
				IBatisDaoUtils.getCountQuery(statementName), dto);
		int totalCount = Integer.parseInt(count.toString());

		dto.setTotalCount(totalCount);
		dto.calStart();
		// dto.setNeedCount(false);
		List rstList = getSqlMapClientTemplate().queryForList(statementName,
				dto);

		BeanUtils.copyProperties(pg, dto);
		pg.setTotalCount(totalCount);
		pg.setResultList(rstList);

		return pg;
	}

}
